import { packageDirectorySync } from 'pkg-dir'
import { readFileSync } from 'fs'
import path from 'path'
import formatPath from '@start-cli-dev/format-path'
import npminstall from 'npminstall'
import { pathExists, pathExistsSync } from 'path-exists'
import fse from 'fs-extra'
import {
  getDefaultRegistry,
  getNpmLastedVersion
} from '@start-cli-dev/get-npm-info'

interface Options {
  targetPath: string
  storeDir: string
  packageName: string
  packageVersion: string
}

export default class Package {
  targetPath: string

  packageName: string

  storeDir: string

  packageVersion: string

  cacheFilepathPrefix: string

  constructor(options?: Options) {
    if (!options) {
      throw new Error('Package 的options不能为空')
    }

    // 定义package 的路径
    this.targetPath = options.targetPath
    // 定义package的储存路径
    this.storeDir = options.storeDir
    this.packageName = options.packageName
    this.packageVersion = options.packageVersion
    this.cacheFilepathPrefix = this.packageName.replace(/\//g, '_')
  }

  async prepare() {
    // storeDir 不存在创建
    if (this.storeDir && !pathExists(this.storeDir)) {
      fse.mkdirpSync(this.storeDir)
    }
    if (this.packageVersion === 'latest') {
      this.packageVersion = await getNpmLastedVersion(this.packageName)
    }
  }

  // 真正的缓存路径是 _@start-cli-dev_init@0.0.15@@start-cli-dev/
  // cnpm 会把同一个组织下的包都放在一起 所以后面拼不拼init都可以
  get cacheFilepath() {
    return path.resolve(
      this.storeDir,
      `_${this.cacheFilepathPrefix}@${this.packageVersion}@${this.packageName}`
    )
  }

  // 获取指定版本模块路径
  getSpecificCacheFilePath(packageVersion: string) {
    return path.resolve(
      this.storeDir,
      `_${this.cacheFilepathPrefix}@${packageVersion}@${this.packageName}`
    )
  }

  // 判断当前package 是否存在
  // eslint-disable-next-line class-methods-use-this
  async exists() {
    if (this.storeDir) {
      await this.prepare()
      // console.log('this.cacheFilepath', this.cacheFilepath)
      return pathExistsSync(this.cacheFilepath)
    }
    return pathExistsSync(this.targetPath)
  }

  // 安装package
  async install() {
    await this.prepare()
    await npminstall({
      root: this.targetPath,
      storeDir: this.storeDir,
      registry: getDefaultRegistry(),
      pkgs: [
        {
          name: this.packageName,
          version: this.packageVersion
        }
      ]
    })
  }

  // 更新package
  // 1. 获取最新的npm版本号
  // 2. 查询最新版本号对应的路径是否存在
  // 3. 如果不存在直接安装最新版本
  async update() {
    await this.prepare()
    const lastedVersion = await getNpmLastedVersion(this.packageName)
    const lastedFilepath = this.getSpecificCacheFilePath(lastedVersion)

    if (!pathExists(lastedFilepath)) {
      await npminstall({
        root: this.targetPath,
        storeDir: this.storeDir,
        registry: getDefaultRegistry(),
        pkgs: [
          {
            name: this.packageName,
            version: lastedVersion
          }
        ]
      })
      this.packageVersion = lastedVersion
    }
  }

  // 获取入口文件
  getRootFilePath() {
    // eslint-disable-next-line no-underscore-dangle
    function _getRootFilePath(targetPath: string) {
      // 1. 获取package.json 所在的目录   pkg-dir
      const dir = packageDirectorySync({ cwd: targetPath })
      if (!dir) return null
      // 2. 读取package.json
      const pkgJson = JSON.parse(readFileSync(`${dir}/package.json`, 'utf-8'))

      // 3. 获取入口文件 main
      if (pkgJson?.module) {
        // 4. 路径兼容(macos\windows)
        return formatPath(path.resolve(dir, pkgJson.module))
      }
      return null
    }
    if (this.storeDir) {
      return _getRootFilePath(this.cacheFilepath)
    }
    return _getRootFilePath(this.targetPath)
  }
}
